#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
//#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
extern char **environ;

#include "../comm/log.hpp"
#include "../comm/util.hpp"

namespace ns_compile
{
    using namespace ns_log;
    using namespace ns_util;
    class Compiler
    {
    public:
        Compiler()
        {}
        ~Compiler()
        {}
    public:
    /******************************************** 
    *    函数功能：完成对路径："./temp/"下，名为filename的文件进行编译功能
    *    返回值：编译成功，true；编译失败，false
    * 
    *    源文件名：filename: "xxx";
    *    编译源文件："xxx.cc"
    *    形成可执行文件："xxx.exe"
    *    形成编译错误文件："xxx.compilerr"
    ***********************************************/
        static bool Compile(const std::string& filename)
        {
            //交给子进程完成对文件的编译
            pid_t pid = fork();
            //创建子进程
            if(pid < 0)
            {
                //创建子进程失败
                LOG(Warning, "编译文件时，创建子进程失败");
                return false;
            }
            else if(pid == 0)
            {
                //子进程。利用系统调用编译文件
                
                //1、处理文件编译失败—> 重定向标准错误
                int fd = open(PathSpliceUtil::Compilerr(filename).c_str(), O_CREAT | O_WRONLY, 0664);
                if(fd < 0)
                {
                    LOG(Warning, "标准错误文件无法打开");
                    exit(1);
                }
                dup2(fd, STDERR_FILENO); //将标准错误重定向到文件fd中
                close(fd);
                
                //2、处理编译文件。进程替换之后，不会替换文件描述符
                // "g++ -o xxx.exe xxx.cc -std=c++17"
                ::execlp("g++", "g++", "-o", PathSpliceUtil::Exe(filename).c_str(),PathSpliceUtil::Src(filename).c_str(), "-D", "COMPILE_ONLINE" , "-std=c++17", nullptr/*不要忘记*/, environ);
                //环境变量带上，放置当前工作环境，和配置不同
                
                //有可能调用失败
                LOG(Warning, "系统调用execlp失败, 编译文件失败");
                exit(2); //进程直接退出
            }
            else
            {
                //父进程
                waitpid(pid, nullptr, 0); //阻塞等待
                //是否编译成功，查看是否对应的可执行文件
                if(FileUtil::IsFileExists(PathSpliceUtil::Exe(filename)))
                {
                    //LOG(Info, "文件[%s]编译成功", filename.c_str());
                    return true;
                }
                LOG(Info, "文件[%s]编译失败", filename.c_str());
                return false;
            }
        } 
    };
}